package net.stogi.unicore.beyond.ui.client.taskeditor.taskoutline;

import net.stogi.common.gwt.dispatch.client.core.ActionManager;
import net.stogi.common.gwt.frame.client.core.EventBus;
import net.stogi.common.gwt.frame.client.mvp.AbstractPresenter;
import net.stogi.common.mvp.core.behavior.Behavior;
import net.stogi.common.mvp.core.behavior.BehaviorWithValue;
import net.stogi.unicore.beyond.api.model.InputDefinition;
import net.stogi.unicore.beyond.api.model.InputGroup;
import net.stogi.unicore.beyond.api.model.OutputDefinition;
import net.stogi.unicore.beyond.api.model.OutputGroup;
import net.stogi.unicore.beyond.api.model.TaskDefinition;
import net.stogi.unicore.beyond.ui.client.taskeditor.taskpreview.InputDefinitionAddedEvent;
import net.stogi.unicore.beyond.ui.client.taskeditor.taskpreview.InputDefinitionAddedHandler;
import net.stogi.unicore.beyond.ui.client.taskeditor.taskpreview.InputGroupAddedEvent;
import net.stogi.unicore.beyond.ui.client.taskeditor.taskpreview.InputGroupAddedHandler;
import net.stogi.unicore.beyond.ui.client.taskeditor.taskpreview.OutputGroupAddedEvent;
import net.stogi.unicore.beyond.ui.client.taskeditor.taskpreview.OutputGroupAddedHandler;
import net.stogi.unicore.beyond.ui.client.taskeditor.taskpreview.TaskPreviewClosedEvent;
import net.stogi.unicore.beyond.ui.client.taskeditor.taskpreview.TaskPreviewClosedHandler;
import net.stogi.unicore.beyond.ui.client.taskeditor.taskpreview.TaskPreviewOpenedEvent;
import net.stogi.unicore.beyond.ui.client.taskeditor.taskpreview.TaskPreviewOpenedHandler;

import com.google.gwt.core.client.GWT;

class TaskOutlinePresenter extends AbstractPresenter {

	private TaskDefinition taskDefinition;

	private TaskOutlineView taskOutlineView;

	public TaskOutlinePresenter(TaskOutlineView taskOutlineView, ActionManager actionManager, EventBus eventBus) {
		super(actionManager, eventBus);
		this.taskOutlineView = taskOutlineView;
		initBehavior();
		initHandlers();
	}

	@Override
	protected void initBehavior() {
		// Input definition was selected in outline.
		taskOutlineView.setInputDefinitionSelectedBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted definition.
					InputDefinition inputDefinition = taskDefinition.selectInputDefinitionWithName(value);
					eventBus.fireEvent(new InputDefinitionSelectedEvent(inputDefinition));

					// Block possibility of moving selected definition.
					internalBlockActions(inputDefinition.getIndex(), taskDefinition.selectInputGroupWithInputDefinition(inputDefinition)
							.getInputDefinitions().size() - 1);
				}
			}
		});
		// Input group was selected in outline.
		taskOutlineView.setInputGroupSelectedBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted group.
					InputGroup inputGroup = taskDefinition.selectInputGroupWithName(value);

					// Block possibility of moving selected group.
					internalBlockActions(inputGroup.getIndex(), taskDefinition.getInputGroups().size() - 1);
					eventBus.fireEvent(new InputGroupSelectedEvent());
				}
			}
		});
		// Input definition was deleted in outline.
		taskOutlineView.setDeleteInputDefinitionBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted definition.
					InputDefinition inputDefinition = taskDefinition.selectInputDefinitionWithName(value);
					InputGroup inputGroup = taskDefinition.selectInputGroupWithInputDefinition(inputDefinition);
					// TODO: Delete input definition.
					if (taskDefinition.removeInputDefinition(inputDefinition)) {
						internalDisplayTaskDefinition(taskDefinition);
						eventBus.fireEvent(new InputDefinitionRemovedEvent(inputDefinition, inputGroup));
					}
				}
			}
		});
		// Input definition was moved down in outline.
		taskOutlineView.setMoveDownInputDefinitionBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted definition.
					InputDefinition inputDefinition = taskDefinition.selectInputDefinitionWithName(value);
					InputGroup inputGroup = taskDefinition.selectInputGroupWithInputDefinition(inputDefinition);
					if (inputGroup.moveInputDefinition(inputDefinition, 1)) {
						internalDisplayTaskDefinition(taskDefinition);
						taskOutlineView.selectInputDefinition(value);
						eventBus.fireEvent(new InputDefinitionMovedEvent(inputDefinition, inputGroup));
					}
				}
			}
		});
		// Input definition was moved up in outline.
		taskOutlineView.setMoveUpInputDefinitionBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted group.
					InputDefinition inputDefinition = taskDefinition.selectInputDefinitionWithName(value);
					InputGroup inputGroup = taskDefinition.selectInputGroupWithInputDefinition(inputDefinition);
					if (inputGroup.moveInputDefinition(inputDefinition, -1)) {
						internalDisplayTaskDefinition(taskDefinition);
						taskOutlineView.selectInputDefinition(value);
						eventBus.fireEvent(new InputDefinitionMovedEvent(inputDefinition, inputGroup));
					}
				}
			}
		});
		// Input group was deleted in outline.
		taskOutlineView.setDeleteInputGroupBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted group.
					InputGroup inputGroup = taskDefinition.selectInputGroupWithName(value);
					if (taskDefinition.removeInputGroup(inputGroup)) {
						internalDisplayTaskDefinition(taskDefinition);
						internalBlockActions();
						eventBus.fireEvent(new InputGroupRemovedEvent(inputGroup));
					}
				}
			}
		});
		// Input group was moved down in outline.
		taskOutlineView.setMoveDownInputGroupBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted group.
					InputGroup inputGroup = taskDefinition.selectInputGroupWithName(value);
					if (taskDefinition.moveInputGroup(inputGroup, 1)) {
						internalDisplayTaskDefinition(taskDefinition);
						taskOutlineView.selectInputGroup(value);
						eventBus.fireEvent(new InputGroupMovedEvent(inputGroup));
					}
				}
			}
		});
		// Input group was moved up in outline.
		taskOutlineView.setMoveUpInputGroupBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted group.
					InputGroup inputGroup = taskDefinition.selectInputGroupWithName(value);
					if (taskDefinition.moveInputGroup(inputGroup, -1)) {
						internalDisplayTaskDefinition(taskDefinition);
						taskOutlineView.selectInputGroup(value);
						eventBus.fireEvent(new InputGroupMovedEvent(inputGroup));
					}
				}
			}
		});
		// Output definition was selected in outline.
		taskOutlineView.setOutputDefinitionSelectedBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted definition.
					OutputDefinition outputDefinition = taskDefinition.selectOutputDefinitionWithName(value);
					eventBus.fireEvent(new OutputDefinitionSelectedEvent(outputDefinition));

					// Block possibility of moving selected definition.
					internalBlockActions(outputDefinition.getIndex(), taskDefinition.selectOutputGroupWithOutputDefinition(outputDefinition)
							.getOutputDefinitions().size() - 1);
				}
			}
		});
		// Output group was selected in outline.
		taskOutlineView.setOutputGroupSelectedBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted group.
					OutputGroup outputGroup = taskDefinition.selectOutputGroupWithName(value);

					// Block possibility of moving selected group.
					internalBlockActions(outputGroup.getIndex(), taskDefinition.getOutputGroups().size() - 1);
					eventBus.fireEvent(new OutputGroupSelectedEvent());
				}
			}
		});
		// Input definition was deleted in outline.
		taskOutlineView.setDeleteOutputDefinitionBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted definition.
					OutputDefinition outputDefinition = taskDefinition.selectOutputDefinitionWithName(value);
					OutputGroup outputGroup = taskDefinition.selectOutputGroupWithOutputDefinition(outputDefinition);
					// TODO: Delete output definition.
					if (taskDefinition.removeOutputDefinition(outputDefinition)) {
						internalDisplayTaskDefinition(taskDefinition);
						eventBus.fireEvent(new OutputDefinitionRemovedEvent(outputDefinition, outputGroup));
					}
				}
			}
		});
		// Output definition was moved down in outline.
		taskOutlineView.setMoveDownOutputDefinitionBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted definition.
					OutputDefinition outputDefinition = taskDefinition.selectOutputDefinitionWithName(value);
					OutputGroup outputGroup = taskDefinition.selectOutputGroupWithOutputDefinition(outputDefinition);
					if (outputGroup.moveOutputDefinition(outputDefinition, 1)) {
						internalDisplayTaskDefinition(taskDefinition);
						taskOutlineView.selectOutputDefinition(value);
						eventBus.fireEvent(new OutputDefinitionMovedEvent(outputDefinition, outputGroup));
					}
				}
			}
		});
		// Output definition was moved up in outline.
		taskOutlineView.setMoveUpOutputDefinitionBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted definition.
					OutputDefinition outputDefinition = taskDefinition.selectOutputDefinitionWithName(value);
					OutputGroup outputGroup = taskDefinition.selectOutputGroupWithOutputDefinition(outputDefinition);
					if (outputGroup.moveOutputDefinition(outputDefinition, -1)) {
						internalDisplayTaskDefinition(taskDefinition);
						taskOutlineView.selectOutputDefinition(value);
						eventBus.fireEvent(new OutputDefinitionMovedEvent(outputDefinition, outputGroup));
					}
				}
			}
		});
		// Output group was deleted in outline.
		taskOutlineView.setDeleteOutputGroupBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted group.
					OutputGroup outputGroup = taskDefinition.selectOutputGroupWithName(value);
					if (taskDefinition.removeOutputGroup(outputGroup)) {
						internalDisplayTaskDefinition(taskDefinition);
						internalBlockActions();
						eventBus.fireEvent(new OutputGroupRemovedEvent(outputGroup));
					}
				}
			}
		});
		// Output group was moved down in outline.
		taskOutlineView.setMoveDownOutputGroupBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted group.
					OutputGroup outputGroup = taskDefinition.selectOutputGroupWithName(value);
					if (taskDefinition.moveOutputGroup(outputGroup, 1)) {
						internalDisplayTaskDefinition(taskDefinition);
						taskOutlineView.selectOutputGroup(value);
						eventBus.fireEvent(new OutputGroupMovedEvent(outputGroup));
					}
				}
			}
		});
		// Output group was moved up in outline.
		taskOutlineView.setMoveUpOutputGroupBehavior(new BehaviorWithValue<String>() {

			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted group.
					OutputGroup outputGroup = taskDefinition.selectOutputGroupWithName(value);
					if (taskDefinition.moveOutputGroup(outputGroup, -1)) {
						internalDisplayTaskDefinition(taskDefinition);
						taskOutlineView.selectOutputGroup(value);
						eventBus.fireEvent(new OutputGroupMovedEvent(outputGroup));
					}
				}
			}
		});
		// Edit Input Definition in the outline.
		taskOutlineView.setEditInputDefinitionBehavior(new BehaviorWithValue<String>() {
			
			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted definition.
					InputDefinition inputDefinition = taskDefinition.selectInputDefinitionWithName(value);
					eventBus.fireEvent(new EditInputDefinitionEvent(inputDefinition));
				}
			}
		});
		// Edit Input Group in the outline.
		taskOutlineView.setEditInputGroupBehavior(new BehaviorWithValue<String>() {
			
			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted definition.
					InputGroup inputGroup = taskDefinition.selectInputGroupWithName(value);
					eventBus.fireEvent(new EditInputGroupEvent(inputGroup));
				}
			}
		});
		// Edit Output Definition in the outline.
		taskOutlineView.setEditOutputDefinitionBehavior(new BehaviorWithValue<String>() {
			
			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted definition.
					OutputDefinition outputDefinition = taskDefinition.selectOutputDefinitionWithName(value);
					eventBus.fireEvent(new EditOutputDefinitionEvent(outputDefinition));
				}
			}
		});
		// Edit Output Group in the outline.
		taskOutlineView.setEditOutputGroupBehavior(new BehaviorWithValue<String>() {
			
			@Override
			public void behave(String value) {
				if (value != null) {
					// Select highlighted definition.
					OutputGroup outputGroup = taskDefinition.selectOutputGroupWithName(value);
					eventBus.fireEvent(new EditOutputGroupEvent(outputGroup));
				}
			}
		});
		// Some other element was selected in outline.
		taskOutlineView.setSomethingElseSelectedBehavior(new Behavior() {

			@Override
			public void behave() {
				internalBlockActions();
			}
		});
	}

	@Override
	protected void initHandlers() {
		// New input definition was added.
		eventBus.addHandler(InputDefinitionAddedEvent.TYPE, new InputDefinitionAddedHandler() {

			@Override
			public void onInputDefinitionAdded(InputDefinitionAddedEvent event) {
				InputDefinition inputDefinition = event.getInputDefinition();
				InputGroup inputGroup = event.getInputGroup();
				int definitionIndex = inputGroup.getInputDefinitions().indexOf(inputDefinition);

				taskOutlineView.addInputDefinition(inputDefinition.getName(), definitionIndex, inputGroup.getIndex());
			}
		});
		// New input group was added.
		eventBus.addHandler(InputGroupAddedEvent.TYPE, new InputGroupAddedHandler() {

			@Override
			public void onInputGroupAdded(InputGroupAddedEvent event) {
				InputGroup inputGroup = event.getInputGroup();

				taskOutlineView.addInputGroup(inputGroup.getName(), inputGroup.getIndex());
			}
		});
		// New output group was added.
		eventBus.addHandler(OutputGroupAddedEvent.TYPE, new OutputGroupAddedHandler() {

			@Override
			public void onOutputGroupAdded(OutputGroupAddedEvent event) {
				OutputGroup outputGroup = event.getOutputGroup();

				taskOutlineView.addOutputGroup(outputGroup.getName(), outputGroup.getIndex());
			}
		});
		// New output definition was added.
		eventBus.addHandler(OutputDefinitionAddedEvent.TYPE, new OutputDefinitionAddedHandler() {

			@Override
			public void onOutputDefinitionAdded(OutputDefinitionAddedEvent event) {
				OutputDefinition outputDefinition = event.getOutputDefinition();
				OutputGroup outputGroup = event.getOutputGroup();
				int definitionIndex = outputGroup.getOutputDefinitions().indexOf(outputDefinition);

				taskOutlineView.addOutputDefinition(outputDefinition.getName(), definitionIndex, outputGroup.getIndex());
			}
		});
		// Task preview was closed.
		eventBus.addHandler(TaskPreviewClosedEvent.TYPE, new TaskPreviewClosedHandler() {

			@Override
			public void onTaskPreviewClosed(TaskPreviewClosedEvent event) {
				taskOutlineView.clear();
			}
		});
		// Task preview was opened.
		eventBus.addHandler(TaskPreviewOpenedEvent.TYPE, new TaskPreviewOpenedHandler() {

			@Override
			public void onTaskPreviewOpened(TaskPreviewOpenedEvent event) {
				taskDefinition = event.getTaskDefinition();
				taskOutlineView.clear();
				internalDisplayTaskDefinition(taskDefinition);
			}
		});
		// Task definition was edited.
		eventBus.addHandler(TaskDefinitionEditedEvent.TYPE, new TaskDefinitionEditedHandler() {

			@Override
			public void onTaskDefinitionEdited(TaskDefinitionEditedEvent event) {
				internalDisplayTaskDefinition(taskDefinition);
			}
		});
	}

	private void internalBlockActions(int currentIndex, int maxIndex) {
		GWT.log("TaskOutlinePresenter.internalBlockActions(): [ currentIndex = '" + currentIndex + "', maxIndex = '" + maxIndex + "' ]");
		// Unblock moving.
		taskOutlineView.blockDelete(false);
		taskOutlineView.blockEdit(false);
		taskOutlineView.blockMoveDown(false);
		taskOutlineView.blockMoveUp(false);
		// Block moving up.
		if (currentIndex <= 0) {
			taskOutlineView.blockMoveUp(true);
		}
		// Block moving down.
		if (currentIndex >= maxIndex) {
			taskOutlineView.blockMoveDown(true);
		}
	}

	private void internalBlockActions() {
		// Unblock moving.
		taskOutlineView.blockDelete(true);
		taskOutlineView.blockEdit(true);
		taskOutlineView.blockMoveDown(true);
		taskOutlineView.blockMoveUp(true);
	}

	private void internalDisplayTaskDefinition(TaskDefinition taskDefinition) {
		taskOutlineView.clear();
		// Display input.
		for (InputGroup inputGroup : taskDefinition.getInputGroups()) {
			taskOutlineView.addInputGroup(inputGroup.getName(), inputGroup.getIndex());
			for (InputDefinition inputDefinition : inputGroup.getInputDefinitions()) {
				taskOutlineView.addInputDefinition(inputDefinition.getName(), inputDefinition.getIndex(), inputGroup.getIndex());
			}
		}
		// Display output.
		for (OutputGroup outputGroup : taskDefinition.getOutputGroups()) {
			taskOutlineView.addOutputGroup(outputGroup.getName(), outputGroup.getIndex());
			for (OutputDefinition outputDefinition : outputGroup.getOutputDefinitions()) {
				taskOutlineView.addOutputDefinition(outputDefinition.getName(), outputDefinition.getIndex(), outputGroup.getIndex());
			}
		}
	}
}
